#include <getopt.h>
//#include <stddef.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define __STR__(x) #x
#define STR(x) __STR__(x)
#define __CAT__(x, y) x##y
#define CAT(x, y) __CAT__(x, y)

#ifndef ESMD_OPTS
#warning "ESMD_OPTS not defined"
#define ESMD_OPTS //OPT(verbose, NOARG, v, int, set1, 0)
#endif
#ifndef ESMD_DESC
#warning "ESMD_DESC not defined"
#define ESMD_DESC "" //OPT(verbose, NOARG, v, int, set1, 0)
#endif
#ifndef ESMD_NAME
#warning "ESMD_NAME not defined"
#define ESMD_NAME "" //OPT(verbose, NOARG, v, int, set1, 0)
#endif
#define OPTTYPE CAT(ESMD_NAME, _opts_t)
#define PRINT_HELP CAT(ESMD_NAME, _print_help)
#define PARSE_ARGS CAT(ESMD_NAME, _parse_args)
#define POSOPT(lname, type, setter, def, desc) type lname;
#define OPT(lname, arg, sname, type, setter, def, desc) type lname;
typedef struct {
  ESMD_OPTS;
} CAT(ESMD_NAME, _opts_t);
#undef OPT
#undef POSOPT

static int set1(const char *x) {
  return 1;
}

static void PRINT_HELP(FILE *out) {
  fprintf(out, "%s\n", ESMD_DESC);
  fprintf(out, "Usage: %s", STR(ESMD_NAME));
#define NOARG 0
#define REARG 1
#define OPT(lname, arg, sname, type, setter, def, desc) fprintf(out, " -%s %s", #sname, #lname);
#define POSOPT(lname, type, setter, def, desc) fprintf(out, " %s", #lname);
  ESMD_OPTS;
#undef OPT
#undef POSOPT
  fprintf(out, "\n");
#define OPT(lname, arg, sname, type, setter, def, desc) \
  fprintf(out, "-%-1s,--%-15s%-60s\n", #sname, #lname, desc);
#define POSOPT(lname, type, setter, def, desc) fprintf(out, "%-20s%-60s\n", #lname, desc);
  ESMD_OPTS
#undef OPT
#undef POSOPT
#undef NOARG
#undef REARG
}
static void PARSE_ARGS(OPTTYPE *opts, int argc, char **argv) {
#define POSOPT(lname, type, setter, def, desc) opts->lname = def;
#define OPT(lname, arg, sname, type, setter, def, desc) opts->lname = def;
  ESMD_OPTS;
#undef OPT
#undef POSOPT

#define POSOPT(lname, type, setter, def, desc)
#define NOARG ""
#define REARG ":"
#define OPT(lname, arg, sname, type, setter, def, desc) #sname arg
  const char *optstr = "" ESMD_OPTS;
#undef NOARG
#undef REARG
#undef OPT

#define NOARG no_argument
#define REARG required_argument
#define OPT(lname, arg, sname, type, setter, def, desc) {#lname, arg, 0, *#sname},
  struct option long_opts[] = {
      ESMD_OPTS{0, 0, 0, 0}};
#undef NOARG
#undef REARG
#undef OPT
#undef POSOPT

#define POSOPT(lname, type, setter, def, desc) +1
#define OPT(lname, arg, sname, type, setter, def, desc)
  char *posarg[0 ESMD_OPTS];
  int poscnt = 0 ESMD_OPTS;
  int poscur = 0;
#undef OPT
#undef POSOPT
  optind = 1;
#define OPT(lname, arg, sname, type, setter, def, desc) \
  else if (c == *#sname) {                              \
    opts->lname = setter(optarg);                       \
  }
#define POSOPT(lname, type, setter, def, desc)
  optind = 1;
  //while (optind < argc) {
  int c;
  while ((c = getopt_long(argc, argv, optstr, long_opts, &optind)) != -1) {
    //printf("%d %d\n", c, optind);
    if (c == '?') {
      PRINT_HELP(stderr);
    }
    ESMD_OPTS else {
      PRINT_HELP(stderr);
    };
  }
  //printf("%d %d\n", c, optind);
#undef OPT
#undef POSOPT
#define POSOPT(lname, type, setter, def, desc) \
  if (optind < argc)                           \
    opts->lname = setter(argv[optind++]);
#define OPT(lname, arg, sname, type, setter, def, desc)
  ESMD_OPTS;
#undef POSOPT
#undef OPT
}